package tdengine

import (
	"database/sql"
	"fmt"
	"lifi"
	"lifi/internal"
	"lifi/plugins/sinks"
	"log"
	"reflect"
	"regexp"
	"strings"
	"time"

	_ "github.com/taosdata/driver-go/v3/taosSql"
)

type TDEngine struct {
	Host       string   `json:"host,omitempty" yaml:"host"`
	Port       int      `json:"port,omitempty" yaml:"port"`
	Database   string   `json:"database,omitempty" yaml:"database"`
	Username   string   `json:"username,omitempty" yaml:"username"`
	Password   string   `json:"password,omitempty" yaml:"password"`
	StableName string   `json:"stableName,omitempty" yaml:"stableName"`
	TName      string   `json:"tname,omitempty" yaml:"tname"`
	TNameTag   string   `json:"tname_tag,omitempty" yaml:"tname_tag"`
	Tags       []string `json:"tags,omitempty" yaml:"tags"`
	Fields     []string `json:"fields,omitempty" yaml:"fields"`
	taos       *sql.DB
	dsn        string
	tagMap     map[string]string
	tags       []string
}

func (t *TDEngine) Init() error {

	t.dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", t.Username, t.Password, t.Host, t.Port, t.Database)
	t.tagMap = make(map[string]string)
	re := regexp.MustCompile(`(\w+)\[(\w+)]`)

	for _, tag := range t.Tags {
		match := re.FindStringSubmatch(tag)
		if len(match) == 3 {
			t.tagMap[match[1]] = match[2]
			t.tags = append(t.tags, match[1])
		}

	}
	// 使用正则表达式提取匹配的内容

	return nil
}

func (t *TDEngine) Close() error {
	return t.taos.Close()
}

func (t *TDEngine) Connect() error {
	taos, err := sql.Open("taosSql", t.dsn)
	if err != nil {
		return err
	}
	t.taos = taos
	t.taos.SetMaxOpenConns(1)
	t.taos.SetMaxIdleConns(1)
	t.taos.SetConnMaxLifetime(0)
	err = t.taos.Ping()
	if err != nil {
		log.Printf("taos ping err %v", err)
		return err
	}

	return nil
}

func (t *TDEngine) Write(dataList ...lifi.Data) error {
	for _, data := range dataList {
		if md, ok := data.(lifi.Metric); ok {
			data = md
			sqlString, err := t.convertInsertSql(md)
			if err != nil {
				log.Printf("convertInsertSql err %v", err)
				continue
			}

			//log.Printf("sqlString:\n %v\n", sqlString)
			_, err = t.taos.Exec(sqlString)
			if err != nil {
				log.Printf("write metric error: %v;sql:[%s]", err, sqlString)
				return err
			}
			//log.Printf("write metric %v", &exec)

		} else {
			log.Printf("not metrics data igonre")
			continue
		}
	}
	return nil
}

func (t *TDEngine) convertInsertSql(m lifi.Metric) (string, error) {
	if m.Time().IsZero() {
		return "", fmt.Errorf("time is 0")
	}
	ts := m.Time().Format(time.RFC3339Nano)

	var (
		tagsList  []string
		fieldList []string
		err       error
	)
	for _, tag := range t.tags {

		tg, ok := m.GetTag(tag)
		if !ok {
			return "", fmt.Errorf("tag %s not found", tag)
		}
		typ, _ := t.tagMap[tag]
		if typ == "string" {
			tg = fmt.Sprintf("'%s'", tg)
		}
		tagsList = append(tagsList, tg)
	}
	tags := strings.Join(tagsList, ",")
	for _, field := range t.Fields {
		v, _ := m.GetField(field)
		vs := ""

		if v == nil {
			vs = "null"
		} else if reflect.TypeOf(v).Kind() == reflect.String {
			vs = fmt.Sprintf("'%s'", v)
		} else {
			vs, err = internal.ToString(v)
			if err != nil {
				return "", err
			}
		}
		fieldList = append(fieldList, vs)
	}
	vals := strings.Join(fieldList, ",")

	tname := m.TName()
	if tname == "" {
		tname = t.TName
	}
	sqlString := fmt.Sprintf(
		"INSERT INTO %s USING %s TAGS(%s) VALUES('%s',%s)",
		tname, m.STableName(), tags, ts, vals)
	return sqlString, nil

}

func init() {
	sinks.Add("tdengine", func() lifi.Sink {
		return &TDEngine{}
	})
}
